#ifndef HANDLEFACTORY_HANDLE_H
#define HANDLEFACTORY_HANDLE_H

///////////////////////////////////////////////////////////////////////////////
//
// Handle.h
//
///////////////////////////////////////////////////////////////////////////////
//
// Author:            Jan Maes
// Created:           Sunday, June 15, 2008
// Responsibilities:  Maintain a count of the handles that point to the same 
//                    object, and delete the object when this count becomes 
//                    zero.
//          
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// Includes
///////////////////////////////////////////////////////////////////////////////

#include "HandleFactoryObjectCreatorImpl.h"
#include "HandleImpl.h"

#include <memory>
#include <utility>

///////////////////////////////////////////////////////////////////////////////
// namespace HandleFactory
///////////////////////////////////////////////////////////////////////////////

namespace HandleFactory
  {

  ///////////////////////////////////////////////////////////////////////////////
  // class Handle
  ///////////////////////////////////////////////////////////////////////////////

  template <class TObject>
  class Handle
    { 
    public:
      Handle();
      explicit Handle(TObject* ip_object);
      template <class TObjectDeleter> explicit Handle(TObject* ip_object, TObjectDeleter i_object_deleter);
      Handle(const Handle<TObject>& i_handle) throw();        
      ~Handle();          
      
      Handle<TObject>& operator = (const Handle<TObject>& i_handle) throw();

      void Swap(Handle<TObject>& i_handle) throw();

      TObject* GetInternalObject() throw();
      const TObject* GetInternalObject() const throw();
      TObject* operator -> () throw();
      const TObject* operator -> () const throw();
      TObject& operator * ();
      const TObject& operator * () const;

      unsigned long GetRefCount() const throw();

      bool IsValid() const throw();
      bool IsUnique() const throw();

      bool operator == (const Handle<TObject>& i_handle) const throw();
      bool operator != (const Handle<TObject>& i_handle) const throw();

    private:
      HandleImplBase<TObject>* mp_handle_impl;
    };

  ///////////////////////////////////////////////////////////////////////////////

  template <class TObject>
  Handle<TObject>::Handle() : 
  mp_handle_impl(new HandleImpl_no_delete<TObject>())
    {
    }

  ///////////////////////////////////////////////////////////////////////////////

  template <class TObject>
  Handle<TObject>::Handle(TObject* ip_object) : 
  mp_handle_impl(new HandleImpl_default_delete<TObject>(ip_object))
    {
    }

  ///////////////////////////////////////////////////////////////////////////////

  template <class TObject>
  template <class TObjectDeleter>
  Handle<TObject>::Handle(TObject* ip_object, TObjectDeleter i_object_deleter) : 
  mp_handle_impl(new HandleImpl_custom_delete<TObject, TObjectDeleter>(ip_object, i_object_deleter))
    {
    }

  ///////////////////////////////////////////////////////////////////////////////

  template <class TObject>
  Handle<TObject>::Handle(const Handle<TObject>& i_handle) throw()
    {
    mp_handle_impl = i_handle.mp_handle_impl;
    mp_handle_impl->RefIncrement();
    }

  ///////////////////////////////////////////////////////////////////////////////

  template <class TObject>
  Handle<TObject>::~Handle()
    {
    mp_handle_impl->RefDecrement();
    if (mp_handle_impl->GetRefCount() == 0)
      {
      mp_handle_impl->DeleteObject();
      delete mp_handle_impl;
      }
    }

  ///////////////////////////////////////////////////////////////////////////////

  template <class TObject>
  Handle<TObject>& Handle<TObject>::operator = (const Handle<TObject>& i_handle) throw()
    {
    Handle<TObject> temp(i_handle);
    Swap(temp);
    return *this;
    }

  ///////////////////////////////////////////////////////////////////////////////

  template <class TObject>
  void Handle<TObject>::Swap(Handle<TObject>& i_handle) throw()
    {
    std::swap(mp_handle_impl, i_handle.mp_handle_impl);
    }

  ///////////////////////////////////////////////////////////////////////////////

  template <class TObject>
  TObject* Handle<TObject>::GetInternalObject() throw()
    {
    return mp_handle_impl->GetInternalObject();
    }

  ///////////////////////////////////////////////////////////////////////////////

  template <class TObject>
  const TObject* Handle<TObject>::GetInternalObject() const throw()
    {
    return mp_handle_impl->GetInternalObject();
    }

  ///////////////////////////////////////////////////////////////////////////////

  template <class TObject>
  TObject* Handle<TObject>::operator -> () throw()
    {
    return mp_handle_impl->GetInternalObject();
    }

  ///////////////////////////////////////////////////////////////////////////////

  template <class TObject>
  const TObject* Handle<TObject>::operator -> () const
    {
    return mp_handle_impl->GetInternalObject();
    }

  ///////////////////////////////////////////////////////////////////////////////

  template <class TObject>
  TObject& Handle<TObject>::operator * ()
    {
    return *(mp_handle_impl->GetInternalObject());
    }

  ///////////////////////////////////////////////////////////////////////////////

  template <class TObject>
  const TObject& Handle<TObject>::operator * () const
    {
    return *(mp_handle_impl->GetInternalObject());
    }

  ///////////////////////////////////////////////////////////////////////////////

  template <class TObject>
  unsigned long Handle<TObject>::GetRefCount() const throw()
    {
    return mp_handle_impl->GetRefCount();
    }

  ///////////////////////////////////////////////////////////////////////////////

  template <class TObject>
  bool Handle<TObject>::IsValid() const throw()
    {
    return mp_handle_impl->IsValid();
    }

  ///////////////////////////////////////////////////////////////////////////////

  template <class TObject>
  bool Handle<TObject>::IsUnique() const throw()
    {
    return (mp_handle_impl->GetRefCount() == 1);
    }

  ///////////////////////////////////////////////////////////////////////////////

  template <class TObject>
  bool Handle<TObject>::operator == (const Handle<TObject>& i_handle) const throw()
    {
    return mp_handle_impl == i_handle.mp_handle_impl;
    }

  ///////////////////////////////////////////////////////////////////////////////

  template <class TObject>
  bool Handle<TObject>::operator != (const Handle<TObject>& i_handle) const throw()
    {
    return !(*this == i_handle);
    }

  ///////////////////////////////////////////////////////////////////////////////

  } // namespace HandleFactory

#endif